Do tej pory komputer zawsze wybierał "kamień", więc kolejne rundy były – delikatnie mówiąc – łatwe do przewidzenia. W tym submodule zajmiemy się losowaniem ruchu komputera oraz umożliwieniem graczowi wyboru własnego ruchu.
Losowanie liczby
Czas na odrobinę matematyki. Będziemy losować ruch komputera, aby nie był to za każdym razem kamień. W JS istnieje wbudowana funkcja pozwalająca na losowanie liczb – jest to Math.random. Ta funkcja losuje liczbę z zakresu od 0 do 0.999... ("prawie 1"). Ten zakres może wydawać się dziwny, ale za chwilę zobaczysz, jak bardzo jest przydatny.
No dobra, ale co zrobić z jakimś wylosowanym ułamkiem, żeby dostać jedno z trzech możliwych zagrań? Zacznijmy od wylosowania liczby 1, 2 lub 3. Skoro Math.random losuje liczbę od 0 do 0.999..., to wystarczy:
- pomnożyć wylosowaną liczbę przez
3, co da nam liczbę z przedziału od 0 do 2.999... ("prawie 3"),
- do tej liczby dodać
1, co da nam liczbę z przedziału od 1 do 3.999... ("prawie 4"),
- zaokrąglić wylosowaną liczbę w dół za pomocą
Math.floor, co da nam liczbę od 1 do 3.
Ten "dziwny przedział" losowej liczby sprawia, że otrzymamy losowy wybór jednej z 3 liczb.
Dla zainteresowanych matematyką
Dzięki temu, że Math.random losuje liczbę z zakresu od 0 do 0.999... ("prawie 1"), możemy wylosowany zakres zaokrąglić w dół, otrzymując zawsze 0. Jeśli dołożymy mnożenie i dodawanie, wedle powyższego schematu, będzie równa szansa na wylosowanie liczb 1, 2 i 3.
Gdyby Math.random losował liczbę z zakresu od 0 do 1, to w zależności od podejścia, albo mielibyśmy bardzo rzadki przypadek wylosowania liczby 4, albo liczba 2 wypadałaby dwukrotnie częściej od pozostałych.
Mamy już algorytm losowania, czas zamienić go na kod!
Przy każdym wykonaniu skryptu zostanie wylosowana nowa liczba. Oczywiście, nasza finalna liczba czasami będzie taka sama liczba kilka razy z rzędu – szybko to zauważysz, ponieważ są tylko trzy możliwe liczby.
Jak już się zapewne domyślasz, w powyższym kodzie znak * oznacza mnożenie, a znak + oznacza dodawanie. Warto zauważyć, że znak + służy w JS zarówno do dodawania, jak i łączenia tekstów. Może to czasem prowadzić do błędów, z którymi spotkasz się w kolejnym module. Pokażemy Ci wtedy jak ich unikać.
W powyższym przykładzie zapisaliśmy osobno losowanie liczby, operacje matematyczne, oraz zaokrąglenie. Zrobiliśmy to, aby łatwiej było Ci zrozumieć kolejne kroki, jednak moglibyśmy zapisać całość jako:
let randomNumber = Math.floor(Math.random() * 3 + 1);
Ćwiczenie
Zmień powyższy kod tak, aby dodatkowo losował liczbę całkowitą od 11 do 19.
Przetestuj swój kod, aby upewnić się, że możliwe jest wylosowanie liczb 11 i 19, ale nigdy nie zostanie wylosowana liczba spoza tego zakresu (np. 7 lub 23).
Jeśli okaże się, że Twój kod działa nieprawidłowo, zastanów się, czy w naszym przykładzie liczba 3 oznacza maksymalną wartość, czy może raczej liczbę możliwych rezultatów.
Nie musisz zapisywać swojego rozwiązania – możesz wykonać to ćwiczenie w zagnieżdżonym edytorze powyżej.
Logika warunkowa if...else
Dość matematyki – czas na trochę logiki. Mamy już wylosowaną liczbę 1, 2 lub 3 i teraz chcemy na tej podstawie wybrać jeden z ruchów: "kamień", "papier", lub "nożyce".
Wykorzystamy do tego wyrażenie if...else, które – w wielkim skrócie – działa na zasadzie "jeśli warunek jest spełniony, zrób to, a w przeciwnym wypadku, zrób tamto".
Już za chwilę nie będziesz sobie wyobrażać świata bez if...else. Jest to podstawa programowania, ponieważ pozwala na to, aby Twój skrypt rozpoznawał okoliczności, w których ma (lub nie ma) wykonać pewne operacje.
W naszej grze if...else pozwoli nam nie tylko na to, aby np. na podstawie wylosowanej liczby 1 otrzymać słowo "kamień". Użyjemy go też do sprawdzania, kto wygrał daną partię. Zacznijmy jednak od podstaw...
Metafora - if...else
Strukturę if...else możemy wyobrazić sobie jako sortownię poczty, która w zależności np. od rozmiaru czy wagi przesyłek kieruje je do różnych ciężarówek. W tej sortowni jest jednak zasada, że można zadawać tylko pytania, na które odpowiedź to "tak" lub "nie". Na przykład:
- Czy paczka ma naklejkę "ostrożnie – szkło"? Jeśli tak, musi jechać ciężarówką A.
- Czy paczka jest standardowych rozmiarów? Jeśli tak, musi jechać ciężarówką B.
- Czy paczka waży ponad 10 kg? Jeśli tak, musi jechać ciężarówką C.
- W przeciwnym wypadku musi jechać ciężarówką D.
Zobaczmy działanie if...else na przykładzie!
if(1 > 2){
printMessage('Niesamowite! Jeden jest większe niż dwa!!!');
} else {
printMessage('Wszystko po staremu, jeden jest mniejsze niż dwa.');
}
Oczywiście, w tym przykładzie sprawdzamy, czy 1 jest większe niż 2, tylko po to, aby zaprezentować, jak działa wyrażenie if...else. Ci z Was, którzy uwielbiają matematykę, zapewne już zdążyli zauważyć nasz błąd – przecież jeśli nie jest prawdą 1 > 2, to wcale nie znaczy, że "jeden jest mniejsze niż dwa". A co, jeśli by były równe? Tu z pomocą przyjdzie nam else if.
if(1 > 2){
printMessage('Niesamowite! Jeden jest większe niż dwa!!!');
} else if (1 == 2) {
printMessage('Dziwne – jeden jest równe dwa?!');
} else {
printMessage('Wszystko po staremu, jeden jest mniejsze niż dwa.');
}
Można stosować wiele bloków else if, a dopiero na końcu można (ale nie trzeba) użyć bloku else. Pamiętaj, że jeśli spełniony zostanie pierwszy warunek (1 > 2), to drugi w ogóle nie zostanie sprawdzony. Tylko kiedy pierwszy warunek będzie fałszywy, sprawdzony zostanie drugi (1 == 2). Jeśli on też będzie fałszywy, zostanie wykonany kod w bloku else.
Zauważ, że do sprawdzenia, czy dwie liczby są sobie równe, użyliśmy wyrażenia ==, czyli dwóch znaków równości. W ten sam sposób możemy sprawdzać, czy dwa teksty są identyczne!
To, że użyliśmy dwóch znaków równości, a nie jednego, jest bardzo ważne (i jest źródłem niezliczonej liczby siwych włosów u początkujących programistów). Bardzo łatwo jest, zamiast == niechcący napisać =, co jest wyrażeniem przypisania wartości do zmiennej. Właśnie za pomocą = ustawialiśmy wartość 'kamień' dla zmiennej computerMove.
let computerMove = 'kamień';
Podsumowując, = i == są zupełnie różnymi wyrażeniami, które niestety wyglądają bardzo podobnie. W kolejnym module przypomnimy o tym i pokażemy, jakie problemy mogą wynikać z tego błędu.
Łączymy losowanie i logikę warunkową
Umiesz już wylosować liczbę, ale w naszej grze potrzebujemy wyświetlić nazwę ruchu, który został wylosowany. Będzie to możliwe dzięki połączeniu wylosowanej liczby z logiką warunkową.
Powyżej wykorzystaliśmy ten sam jednolinijkowy kod losowania liczby, który pokazywaliśmy wcześniej w tym module. Pod nim zapisaliśmy początkową wartość computerMove równą 'nieznany ruch', a następnie dodaliśmy warunek, który sprawdza, czy wylosowano liczbę 1 – jeśli tak, to zmienna computerMove będzie miała nową wartość: 'kamień'.
A co jeśli wylosowana liczba będzie inna? Dlaczego wtedy wyświetla się "nieznany ruch"? Rozwiązanie tego problemu już za chwilę będzie Twoim zadaniem!
Konsola i console.log
Bardzo często w trakcie pisania kodu będziemy korzystać z funkcji console.log, aby debugować nasz kod i lepiej rozumieć jak działa.
Jak widzisz, console.log nie wyświetla niczego na stronie. Zamiast tego, komunikaty wyświetlane za pomocą tej funkcji znajdziesz w konsoli znajdującej się wśród narzędzi developerskich.
Otwórz narzędzia developerskie – możesz to zrobić za pomocą klawisza F12 lub kliknąć prawym przyciskiem na dowolny element na stronie i wybrać "Zbadaj element". Następnie, w panelu narzędzi developerskich, przełącz się z zakładki Elements na Console.
Dzięki zastosowaniu console.log nie będziemy zaśmiecać sobie widoku gry. Wszelkie informacje, które pomogą nam w rozumieniu kodu lub rozwiązywaniu problemu będziemy od teraz wyświetlać w konsoli.
Zapisywanie odpowiedzi gracza
Mamy już wylosowane zagranie komputera, teraz czas na interakcję z graczem. Dzięki temu będzie mógł wybrać swój ruch.
Wykorzystamy do tego funkcję prompt, która wyświetli okienko z polem na wpisanie wartości. Dla uproszczenia poprosimy gracza o wpisanie liczby 1, 2 lub 3.
Poniższy przykład wyświetli to okienko bezpośrednio po uruchomieniu kodu. Dlatego jego kod będzie widoczny dopiero po kliknięciu "Run Pen".
Ten kod wygląda bardzo podobnie do wcześniejszego, w którym losowaliśmy ruch komputera. Różni się jedynie nazwami zmiennych oraz sposobem ustalenia liczby 1-3. W przypadku komputera losowaliśmy tę liczbę, a w tym przypadku liczbę podaje gracz.
Podobnie jak wcześniej, skrypt rozpoznaje tylko wybór kamienia – pozostałe ruchy pozostają jako "nieznany ruch". Za chwilę jednak rozwiążesz również ten problem!
Zaczynamy zbliżać się do pierwszej wersji, którą będzie już można nazwać grą. Na razie losujemy ruch komputera i pytamy gracza o jego ruch. W następnym submodule zajmiemy się ustaleniem, kto wygrał rozgrywkę!